// 2013年版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package types

import (
	"bytes"
	"fmt"
	"go/constant"
	"go/token"
)

// 对象描述命名语言实体，如包，
// 常量、类型、变量、函数（包括方法）或标签。
// 所有对象都实现对象接口。
// None
type Object interface {
	Parent() *Scope // 声明此对象的范围；方法和结构字段为nil
	Pos() token.Pos // 对象标识符在声明中的位置
	Pkg() *Package  // 此对象所属的包；宇宙范围内的标签和对象为零
	Name() string   // 包本地对象名
	Type() Type     // 对象类型
	Exported() bool // 报告名称是否以大写字母开头
	Id() string     // 对象名称（如果已导出），限定名称（如果未导出）（请参见func Id）

	// 字符串返回对象的可读字符串。
	String() string

	// 顺序反映包级别对象的源顺序：if对象
	// a在源中的对象b之前，然后是a.order（）<b.order（）。
	// order为包级对象返回大于0的值；它回来了
	// 0表示所有其他对象（包括文件作用域中的对象）。
	order() uint32

	// 颜色返回对象的颜色。
	color() color

	// setType设置对象的类型。
	setType(Type)

	// setOrder设置对象的顺序号。它必须大于0。
	setOrder(uint32)

	// setColor设置对象的颜色。它不能是白色的。
	setColor(color color)

	// setParent设置对象的父范围。
	setParent(*Scope)

	// sameId报告obj.Id（）和Id（pkg，name）是否相同。
	sameId(pkg *Package, name string) bool

	// scopePos返回此对象范围的起始位置
	scopePos() token.Pos

	// setScopePos设置此对象范围的起始位置。
	setScopePos(pos token.Pos)
}

// 如果导出Id，则返回名称，否则返回
// 返回用包路径限定的名称。
func Id(pkg *Package, name string) string {
	if token.IsExported(name) {
		return name
	}
	// 未报告的名称需要包路径进行区分
	// （如果没有套餐，请确保我们不要以“.”开头
	// 因为这可能会改变设置之间的方法顺序
	// 包内包外-这会破坏一些
	// 测试）
	path := "_"
	// 对于宇宙范围内的对象和可能的类型，pkg为nil
	// 通过Eval引入（另请参见object.sameId中的注释）
	if pkg != nil && pkg.path != "" {
		path = pkg.path
	}
	return path + "." + name
}

// 对象实现对象的公共部分。
type object struct {
	parent    *Scope
	pos       token.Pos
	pkg       *Package
	name      string
	typ       Type
	order_    uint32
	color_    color
	scopePos_ token.Pos
}

// 颜色编码对象的颜色（有关详细信息，请参见Checker.objDecl）。
type color uint32

// 一个物体可以画成三种颜色中的一种。
// 除白色或黑色以外的颜色值被视为灰色。
const (
	white color = iota
	black
	grey // 必须>白色和黑色
)

func (c color) String() string {
	switch c {
	case white:
		return "white"
	case black:
		return "black"
	default:
		return "grey"
	}
}

// colorFor返回对象的（初始）颜色，具体取决于
// 它的t型是否已知。
func colorFor(t Type) color {
	if t != nil {
		return black
	}
	return white
}

// 父级返回声明对象的范围。
// 方法和结构字段的结果为零。
func (obj *object) Parent() *Scope { return obj.parent }

// Pos返回对象标识符的声明位置。
func (obj *object) Pos() token.Pos { return obj.pos }

// Pkg返回对象所属的包。
// 宇宙范围内的标签和对象的结果为零。
func (obj *object) Pkg() *Package { return obj.pkg }

// Name返回对象的（包本地、非限定）名称。
func (obj *object) Name() string { return obj.name }

// 类型返回对象的类型。
func (obj *object) Type() Type { return obj.typ }

// 导出报告是否导出对象（以大写字母开头）。
// 它不考虑对象是否在本地（函数）范围内
// 或者不是。
func (obj *object) Exported() bool { return token.IsExported(obj.name) }

// Id是Id（obj.Pkg（），obj.Name（））的包装器。
func (obj *object) Id() string { return Id(obj.pkg, obj.name) }

func (obj *object) String() string      { panic("abstract") }
func (obj *object) order() uint32       { return obj.order_ }
func (obj *object) color() color        { return obj.color_ }
func (obj *object) scopePos() token.Pos { return obj.scopePos_ }

func (obj *object) setParent(parent *Scope)   { obj.parent = parent }
func (obj *object) setType(typ Type)          { obj.typ = typ }
func (obj *object) setOrder(order uint32)     { assert(order > 0); obj.order_ = order }
func (obj *object) setColor(color color)      { assert(color != white); obj.color_ = color }
func (obj *object) setScopePos(pos token.Pos) { obj.scopePos_ = pos }

func (obj *object) sameId(pkg *Package, name string) bool {
	// 规格：
	// “如果两个标识符的拼写不同，则它们是不同的，
	// 或者，如果它们出现在不同的包中且未导出。
	// 否则，它们是一样的。”
	if name != obj.name {
		return false
	}
	// 对象名称==名称
	if obj.Exported() {
		return true
	}
	// 未导出，因此包必须相同（对于
	// 宇宙范围内的场；这只能发生在类型上
	// 通过Eval引入）
	if pkg == nil || obj.pkg == nil {
		return pkg == obj.pkg
	}
	// 包装！=无和&obj.pkg！=无
	return pkg.path == obj.pkg.path
}

// PkgName表示导入的Go包。
// PkgNames没有类型。
type PkgName struct {
	object
	imported *Package
	used     bool // 设置是否使用了包
}

// NewPkgName返回表示导入包的新PkgName对象。
// 其余参数设置所有对象的属性。
func NewPkgName(pos token.Pos, pkg *Package, name string, imported *Package) *PkgName {
	return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], 0, black, token.NoPos}, imported, false}
}

// Imported返回已导入的包。
// 它与Pkg（）不同，Pkg（）是包含import语句的包。
func (obj *PkgName) Imported() *Package { return obj.imported }

// 常量表示声明的常量。
type Const struct {
	object
	val constant.Value
}

// NewConst返回一个值为val的新常量。
// 其余参数设置所有对象的属性。
func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val constant.Value) *Const {
	return &Const{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}, val}
}

// Val返回常量的值。
func (obj *Const) Val() constant.Value { return obj.val }

func (*Const) isDependency() {} // 常量可以是初始化表达式的依赖项

// TypeName表示（已定义或别名）类型的名称。
type TypeName struct {
	object
}

// NewTypeName返回表示给定类型的新类型名。
// 其余参数设置所有对象的属性。
// None
// typ参数可以是已定义（命名）类型或别名类型。
// 它也可以是nil，以便返回的TypeName可以用作
// 参数，该参数将TypeName的类型设置为边-
// 效应
func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
	return &TypeName{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}}
}

// IsAlias报告obj是否是类型的别名。
func (obj *TypeName) IsAlias() bool {
	switch t := obj.typ.(type) {
	case nil:
		return false
	case *Basic:
		// 不安全。指针不是别名。
		if obj.pkg == Unsafe {
			return false
		}
		// 基本类型的任何用户定义的类型名都是基本类型的别名
		// 基本类型（因为基本类型是在宇宙中预先声明的
		// 范围，任何包范围之外），以及任何具有
		// 与所引用的基本类型的名称不同的名称。
		// 此外，我们需要寻找“字节”和“符文”，因为它们
		// 是别名，但名称相同（以获得更好的错误消息）。
		return obj.pkg != nil || t.name != obj.name || t == universeByte || t == universeRune
	case *Named:
		return obj != t.obj
	default:
		return true
	}
}

// 变量表示声明的变量（包括函数参数和结果以及结构字段）。
type Var struct {
	object
	embedded bool // 如果设置，则该变量是一个嵌入式结构字段，name是类型名称
	isField  bool // var是结构字段
	used     bool // 设置是否使用了变量
}

// NewVar返回一个新变量。
// 参数设置所有对象的属性。
func NewVar(pos token.Pos, pkg *Package, name string, typ Type) *Var {
	return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}}
}

// NewParam返回表示函数参数的新变量。
func NewParam(pos token.Pos, pkg *Package, name string, typ Type) *Var {
	return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}, used: true} // 参数始终“使用”
}

// NewField返回表示结构字段的新变量。
// 对于嵌入字段，名称是非限定类型名称
// /在该字段下可以访问该字段。
func NewField(pos token.Pos, pkg *Package, name string, typ Type, embedded bool) *Var {
	return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}, embedded: embedded, isField: true}
}

// 匿名报告变量是否为嵌入字段。
// 与嵌入式相同；仅用于向后兼容。
func (obj *Var) Anonymous() bool { return obj.embedded }

// Embedded报告变量是否为嵌入字段。
func (obj *Var) Embedded() bool { return obj.embedded }

// IsField报告变量是否为结构字段。
func (obj *Var) IsField() bool { return obj.isField }

func (*Var) isDependency() {} // 变量可以是初始化表达式的依赖项

// Func表示声明的函数、具体方法或抽象
// （接口）方法。其类型（）始终是*签名。
// 由于嵌入，抽象方法可能属于许多接口。
type Func struct {
	object
	hasPtrRecv bool // 仅对尚未具有类型的方法有效
}

// NewFunc返回具有给定签名的新函数，表示
// 函数的类型。
func NewFunc(pos token.Pos, pkg *Package, name string, sig *Signature) *Func {
	// 不要存储（键入的）零签名
	var typ Type
	if sig != nil {
		typ = sig
	}
	return &Func{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}, false}
}

// FullName返回包或接收器类型的限定名称
// 函数或方法obj。
func (obj *Func) FullName() string {
	var buf bytes.Buffer
	writeFuncName(&buf, obj, nil)
	return buf.String()
}

// Scope返回函数体块的作用域。
func (obj *Func) Scope() *Scope { return obj.typ.(*Signature).scope }

func (*Func) isDependency() {} // 函数可以是初始化表达式的依赖项

// 标签表示已声明的标签。
// 标签没有类型。
type Label struct {
	object
	used bool // 设置是否使用了标签
}

// NewLabel返回一个新标签。
func NewLabel(pos token.Pos, pkg *Package, name string) *Label {
	return &Label{object{pos: pos, pkg: pkg, name: name, typ: Typ[Invalid], color_: black}, false}
}

// 内置函数表示内置函数。
// 内置项没有有效的类型。
type Builtin struct {
	object
	id builtinId
}

func newBuiltin(id builtinId) *Builtin {
	return &Builtin{object{name: predeclaredFuncs[id].name, typ: Typ[Invalid], color_: black}, id}
}

// Nil表示预先声明的值Nil。
type Nil struct {
	object
}

func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
	var tname *TypeName
	typ := obj.Type()

	switch obj := obj.(type) {
	case *PkgName:
		fmt.Fprintf(buf, "package %s", obj.Name())
		if path := obj.imported.path; path != "" && path != obj.name {
			fmt.Fprintf(buf, " (%q)", path)
		}
		return

	case *Const:
		buf.WriteString("const")

	case *TypeName:
		tname = obj
		buf.WriteString("type")

	case *Var:
		if obj.isField {
			buf.WriteString("field")
		} else {
			buf.WriteString("var")
		}

	case *Func:
		buf.WriteString("func ")
		writeFuncName(buf, obj, qf)
		if typ != nil {
			WriteSignature(buf, typ.(*Signature), qf)
		}
		return

	case *Label:
		buf.WriteString("label")
		typ = nil

	case *Builtin:
		buf.WriteString("builtin")
		typ = nil

	case *Nil:
		buf.WriteString("nil")
		return

	default:
		panic(fmt.Sprintf("writeObject(%T)", obj))
	}

	buf.WriteByte(' ')

	// 对于包级对象，请限定名称。
	if obj.Pkg() != nil && obj.Pkg().scope.Lookup(obj.Name()) == obj {
		writePackage(buf, obj.Pkg(), qf)
	}
	buf.WriteString(obj.Name())

	if typ == nil {
		return
	}

	if tname != nil {
		// 我们有一个类型对象：不要再为它打印任何内容
		// 基本类型，因为没有更多信息（名称
		// 都是一样的,；另请参见TypeName.IsAlias中的注释）。
		if _, ok := typ.(*Basic); ok {
			return
		}
		if tname.IsAlias() {
			buf.WriteString(" =")
		} else {
			typ = under(typ)
		}
	}

	buf.WriteByte(' ')
	WriteType(buf, typ, qf)
}

func writePackage(buf *bytes.Buffer, pkg *Package, qf Qualifier) {
	if pkg == nil {
		return
	}
	var s string
	if qf != nil {
		s = qf(pkg)
	} else {
		s = pkg.Path()
	}
	if s != "" {
		buf.WriteString(s)
		buf.WriteByte('.')
	}
}

// ObjectString返回obj的字符串形式。
// 限定符控制打印
// 包级对象，并且可以为零。
func ObjectString(obj Object, qf Qualifier) string {
	var buf bytes.Buffer
	writeObject(&buf, obj, qf)
	return buf.String()
}

func (obj *PkgName) String() string  { return ObjectString(obj, nil) }
func (obj *Const) String() string    { return ObjectString(obj, nil) }
func (obj *TypeName) String() string { return ObjectString(obj, nil) }
func (obj *Var) String() string      { return ObjectString(obj, nil) }
func (obj *Func) String() string     { return ObjectString(obj, nil) }
func (obj *Label) String() string    { return ObjectString(obj, nil) }
func (obj *Builtin) String() string  { return ObjectString(obj, nil) }
func (obj *Nil) String() string      { return ObjectString(obj, nil) }

func writeFuncName(buf *bytes.Buffer, f *Func, qf Qualifier) {
	if f.typ != nil {
		sig := f.typ.(*Signature)
		if recv := sig.Recv(); recv != nil {
			buf.WriteByte('(')
			if _, ok := recv.Type().(*Interface); ok {
				// gcimporter创建抽象的
				// 使用接口类型命名的接口
				// （不是指定的类型）作为接收方。
				// 不要把它全部打印出来。
				buf.WriteString("interface")
			} else {
				WriteType(buf, recv.Type(), qf)
			}
			buf.WriteByte(')')
			buf.WriteByte('.')
		} else if f.pkg != nil {
			writePackage(buf, f.pkg, qf)
		}
	}
	buf.WriteString(f.name)
}
